package com.fulihui.usercore.biz.shard.impl;

import com.fulihui.usercore.biz.shard.UserManager;
import com.fulihui.usercore.biz.shard.UserTokenManager;
import com.fulihui.usercore.common.util.AppProps;
import com.fulihui.usercore.common.util.Errors;
import com.fulihui.usercore.common.util.PasswordUtil;
import com.fulihui.usercore.constants.WechatExtReqKeys;
import com.fulihui.usercore.core.processor.auth.AuthProcessorRegister;
import com.fulihui.usercore.core.processor.vcode.VcodeProcessor;
import com.fulihui.usercore.core.processor.vcode.VcodeProcessorRegistor;
import com.fulihui.usercore.core.repository.*;
import com.fulihui.usercore.dal.dataobj.LoginDO;
import com.fulihui.usercore.dal.mapper.OldUserMapper;
import com.fulihui.usercore.dal.mapper.WechatUserMapper;
import com.fulihui.usercore.dto.TagDTO;
import com.fulihui.usercore.dto.UserDTO;
import com.fulihui.usercore.enums.UserRegisterTypeEnum;
import com.fulihui.usercore.enums.VcodeProcessTypeEnum;
import com.fulihui.usercore.enums.WechatOpenIdTypeEnum;
import com.fulihui.usercore.request.auth.QuickAuthRequest;
import com.fulihui.usercore.request.auth.WechatAuthRequest;
import com.fulihui.usercore.request.user.*;
import com.fulihui.usercore.request.vcode.VcodeCheckRequest;
import org.apache.commons.lang3.RandomStringUtils;
import org.near.servicesupport.error.InvokeServiceException;
import org.near.toolkit.common.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

import static org.near.servicesupport.error.Errors.Commons.REQUEST_PARAMETER_ERROR;
import static org.near.servicesupport.error.Errors.Commons.SYSTEM_ERROR;

/**
 * @author Willard.Hu on 2016/5/19.
 */
@Component
public class UserManagerImpl implements UserManager {
    private static final Logger LOG = LoggerFactory.getLogger(UserManagerImpl.class);
    @Autowired
    @Lazy
    private AuthProcessorRegister  authProcessorRegistor;
    @Autowired
    private WechatUserMapper       wechatUserMapper;
    @Autowired
    private UserLoginRepository    userLoginRepository;
    @Autowired
    private OldUserMapper          oldUserMapper;
    @Autowired
    private UserRepository         userRepository;
    @Autowired
    private UserTokenManager       userTokenManager;
    @Autowired
    private VcodeProcessorRegistor vcodeProcessorRegistor;
    @Autowired
    private WechatUserRepository   wechatUserRepository;
    @Autowired
    private LoginHelpRepository loginHelpRepository;
    @Autowired
    private WechatOpenidHelpRepository wechatOpenidHelpRepository;
    @Autowired
    private AppProps               appProps;

    @Override
    public String register(UserRegisterRequest request) {
        String userId;
        if (UserRegisterTypeEnum.MOBILE_QUICK.getCode().equals(request.getRegisterType())) {
            QuickAuthRequest authRequest = new QuickAuthRequest();
            authRequest.setLoginId(request.getLoginId());
            authRequest.setVcode(request.getVcode());
            authRequest.setVcodeNamespace(request.getVcodeNamespace());
            authRequest.setLatitude(request.getLatitude());
            authRequest.setLongitude(request.getLongitude());
            authRequest.setUserIP(request.getUserIP());
            authRequest.setRegisterSource(StringUtil.defaultIfEmpty(request.getRegisterSource()));
            authRequest.setProvince(request.getProvince());
            authRequest.setCity(request.getCity());
            userId = authProcessorRegistor.getProcessor(request.getRegisterType()).login(authRequest);

        } else if (UserRegisterTypeEnum.WECHAT.getCode().equals(request.getRegisterType())) {
            WechatAuthRequest wechatAuthRequest = new WechatAuthRequest();
            wechatAuthRequest.setLoginId(request.getLoginId());
            wechatAuthRequest.setVcode(request.getVcode());
            wechatAuthRequest.setVcodeNamespace(request.getVcodeNamespace());
            wechatAuthRequest.setLatitude(request.getLatitude());
            wechatAuthRequest.setLongitude(request.getLongitude());
            wechatAuthRequest.setUserIP(request.getUserIP());
            wechatAuthRequest.setRegisterSource(StringUtil.defaultIfEmpty(request.getRegisterSource()));
            wechatAuthRequest.setProvince(request.getProvince());
            wechatAuthRequest.setCity(request.getCity());

            // 手机这个版本先使用快捷登陆注册用户，再更新用户信息
            // 所以这里不用考虑 App openid 场景
            wechatAuthRequest.setUnionid((String) request.getExtReq().get(WechatExtReqKeys.UNIONID_KEY));
            wechatAuthRequest.setOpenid(request.getOutUniqueId());
            wechatAuthRequest.setOpenidType(WechatOpenIdTypeEnum.WECHAT_MP.getCode());
            wechatAuthRequest.setNickname((String) request.getExtReq().get(WechatExtReqKeys.NICKNAME_KEY));
            wechatAuthRequest.setHeadImg((String) request.getExtReq().get(WechatExtReqKeys.HEADIMGURL_KEY));

            Object subscribeObj = request.getExtReq().get(WechatExtReqKeys.SUBSCRIBE_KEY);
            Integer subscribe = null;
            if (subscribeObj instanceof Integer) {
                subscribe = (Integer) subscribeObj;
            } else if (subscribeObj instanceof Long) {
                subscribe = ((Long) subscribeObj).intValue();
            }
            wechatAuthRequest.setSubscribe(subscribe == null ? 0 : subscribe);

            Long subscribeTime = (Long) request.getExtReq().get(WechatExtReqKeys.SUBSCRIBE_TIME_KEY);
            wechatAuthRequest.setSubscribeTime(subscribeTime == null ? 0 : subscribeTime);

            Object sexObj = request.getExtReq().get(WechatExtReqKeys.SEX_KEY);
            Integer sex = null;
            if (sexObj instanceof Integer) {
                sex = (Integer) sexObj;
            } else if (sexObj instanceof Long) {
                sex = ((Long) sexObj).intValue();
            }
            wechatAuthRequest.setSex(sex == null ? 0 : 1);

            userId = authProcessorRegistor.getProcessor(request.getRegisterType()).register(wechatAuthRequest);
        } else {
            throw new UnsupportedOperationException("Register type " + request.getRegisterType());
        }
        return userId;
    }

    @Override
    public String login(UserLoginRequest request) {
        String userId;
        if (UserRegisterTypeEnum.MOBILE_QUICK.getCode().equals(request.getRegisterType())) {
            QuickAuthRequest authRequest = new QuickAuthRequest();
            authRequest.setLoginId(request.getLoginId());
            authRequest.setVcode(request.getVcode());
            authRequest.setVcodeNamespace(request.getVcodeNamespace());
            authRequest.setLatitude(request.getLatitude());
            authRequest.setLongitude(request.getLongitude());
            authRequest.setUserIP(request.getUserIP());

            userId = authProcessorRegistor.getProcessor(request.getRegisterType()).login(authRequest);

        } else if (UserRegisterTypeEnum.WECHAT.getCode().equals(request.getRegisterType())) {
            WechatAuthRequest wechatAuthRequest = new WechatAuthRequest();
            wechatAuthRequest.setLoginId(request.getLoginId());
            wechatAuthRequest.setVcode(request.getVcode());
            wechatAuthRequest.setVcodeNamespace(request.getVcodeNamespace());
            wechatAuthRequest.setLatitude(request.getLatitude());
            wechatAuthRequest.setLongitude(request.getLongitude());
            wechatAuthRequest.setUserIP(request.getUserIP());
            // 手机这个版本先使用快捷登陆注册用户，再更新用户信息
            // 所以这里不用考虑 App openid 场景
            wechatAuthRequest.setOpenid(request.getOutUniqueId());
            wechatAuthRequest.setOpenidType(WechatOpenIdTypeEnum.WECHAT_MP.getCode());

            userId = authProcessorRegistor.getProcessor(request.getRegisterType()).login(wechatAuthRequest);

        } else {
            throw new UnsupportedOperationException("Register type " + request.getRegisterType());
        }
        return userId;
    }

    @Override
    public boolean checkRegistered(UserCheckRegisteredRequest request) {
        // 废弃的类型 MOBILE 转换成 MOBILE_QUICK
        if (UserRegisterTypeEnum.MOBILE.getCode().equals(request.getRegisterType())) {
            request.setRegisterType(UserRegisterTypeEnum.MOBILE_QUICK.getCode());
        }
        boolean registered;
        if (UserRegisterTypeEnum.MOBILE_QUICK.getCode().equals(request.getRegisterType())) {
            registered = authProcessorRegistor.getProcessor(request.getRegisterType())
                    .registered(request.getLoginId());
        } else {
            registered = wechatOpenidHelpRepository.querySingle(request.getOutUniqueId(),
                    WechatOpenIdTypeEnum.WECHAT_MP.getCode(), appProps.getWechatMpAppid()) != null;
        }
        return registered;
    }

    @Override
    @Transactional
    public void modifyPasswd(UserModifyPasswdRequest request) {
        LoginDO saved = userLoginRepository.queryByUserId(request.getUserId());
        if (saved == null) {
            LOG.error("没有找到账号，userid:{}", request.getUserId());
            throw new InvokeServiceException(REQUEST_PARAMETER_ERROR);
        }
        boolean check = PasswordUtil.passwordsMatch(saved.getLoginId(), request.getOldPwd(),
                saved.getSalt(), saved.getPassword());
        if (!check) {
            throw new InvokeServiceException(Errors.User.MODIFY_PASSWD_VALID_OLDPASSWD_FAILED);
        }
        updatePassword(saved.getLoginId(), saved.getSalt(), request.getNewPwd());
    }

    @Override
    @Transactional
    public void forgetPasswd(UserForgetPasswdRequest request) {
        LoginDO loginDO = loginHelpRepository.queryByPK(request.getLoginId());
        if (loginDO == null) {
            LOG.error("没有找到账号，loginId:{}", request.getLoginId());
            throw new InvokeServiceException(REQUEST_PARAMETER_ERROR);
        }
        LoginDO saved = userLoginRepository.queryByUserId(loginDO.getUserId());
        if (saved == null) {
            LOG.error("没有找到账号，loginId:{}", request.getLoginId());
            throw new InvokeServiceException(REQUEST_PARAMETER_ERROR);
        }
        VcodeProcessor vcodeProcessor = vcodeProcessorRegistor.getProcessor(VcodeProcessTypeEnum.MOBILE_SMS);
        VcodeCheckRequest checkRequest = new VcodeCheckRequest();
        checkRequest.setAccountNo(saved.getLoginId());
        checkRequest.setVcode(request.getVcode());
        checkRequest.setVcodeNamespace(request.getVcodeNamespace());
        checkRequest.setProcessType(VcodeProcessTypeEnum.MOBILE_SMS.getCode());
        boolean check = vcodeProcessor.check(checkRequest);
        if (!check) {
            throw new InvokeServiceException(Errors.User.MODIFY_PASSWD_VALID_VCODE_FAILED);
        }
        updatePassword(saved.getLoginId(), saved.getSalt(), request.getNewPwd());
    }

    private void updatePassword(String loginId, String salt, String password) {
        LoginDO modify = new LoginDO();
        //// FIXME: 2017/6/1 每次修改密码  更新盐
//        if (StringUtil.isBlank((salt))) {
//            salt = RandomStringUtils.randomAlphanumeric(16);
//        }
        salt = RandomStringUtils.randomAlphanumeric(16);
        modify.setLoginId(loginId);
        modify.setSalt(salt);
        modify.setPassword(PasswordUtil.encryptPassword(loginId, password, salt));
        boolean rs = userLoginRepository.update(modify);
        if (!rs) {
            throw new InvokeServiceException(Errors.User.USER_MODIFY_FAILED);
        }
    }

    @Override
    public boolean hasPasswd(String userId) {
        LoginDO loginDO = userLoginRepository.queryByUserId(userId);
        return loginDO != null && StringUtil.isNotBlank(loginDO.getPassword());
    }

    @Override
    public void resetTags(String userId, List<TagDTO> tags) {
        UserDTO update = new UserDTO();
        update.setUserId(userId);
        if (!CollectionUtils.isEmpty(tags)) {
            update.setTagIds(tags.stream().map(TagDTO::getId).collect(Collectors.toList()));
        } else {
            update.setTagIds(null);
        }
        boolean b = userRepository.modify(update);
        if (!b) {
            throw new InvokeServiceException(SYSTEM_ERROR);
        }
    }

    @Override
    public void setPasswd(UserSetPasswdRequest request) {
        LoginDO saved = userLoginRepository.queryByPK(request.getLoginId());
        if (saved == null) {
            LOG.error("没有找到账号，loginId:{}", request.getLoginId());
            throw new InvokeServiceException(REQUEST_PARAMETER_ERROR);
        }
        updatePasswordByUserId(request.getLoginId(),saved.getUserId(), saved.getSalt(), request.getPasswd());
    }

    /**
     * 根据userid 更新密码
     * @param userId
     * @param salt
     * @param password
     */
    private void updatePasswordByUserId(String loginId,String userId, String salt, String password) {
        LoginDO modify = new LoginDO();
        salt = RandomStringUtils.randomAlphanumeric(16);
        modify.setUserId(userId);
        modify.setSalt(salt);
        modify.setPassword(PasswordUtil.encryptPassword(loginId, password, salt));
        boolean rs = userLoginRepository.updatePasswordByUserId(modify);
        if (!rs) {
            throw new InvokeServiceException(Errors.User.USER_MODIFY_FAILED);
        }
    }
}
